package com.ktjy.aop;

import com.alibaba.fastjson.JSON;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.Arrays;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * SwaggerLogAspect 自动记录Swagger接口日志
 *
 * @author LILIBO
 * @since 2025-03-02
 */
@Slf4j
@Aspect
@Component
@EnableAspectJAutoProxy(proxyTargetClass = true) // 新增AOP代理配置
public class SwaggerLogAspect {

    /**
     * HTTP头部令牌参数Token
     */
    private static final String KEY_TOKEN = "token";

    private static final String STR = "";
    private static final String STR_ = "-";
    private static final String STR_n = "\n";
    private static final String STR_d = ".";
    private static final String STR_ms = " ms";
    private static final String STR_empty = "[]";
    private static final String STR________Line = "\n------------------------------------------------------------------------------------------------";
    private static final String STR_Request_URL = "\n--> Request URL    : ";
    private static final String STR_Operation = "\n--> Operation      : ";
    private static final String STR_HTTP_Method = "\n--> HTTP Method    : ";
    private static final String STR_HeaderToken = "\n--> Header Token   : ";
    private static final String STR_ClassMethod = "\n--> Class Method   : ";
    private static final String STR__IP_Address = "\n--> IP Address     : ";
    private static final String STR_RequestArgs = "\n--> Request Args   : ";
    private static final String STR_ResponseBody = "\n<-- Response Body  : ";
    private static final String STR_Throwable = "\n<-- Throwable      : ";
    private static final String STR_TimeConsuming = "\n<-- Time Consuming : ";

    /**
     * 使用环绕增强自动记录标记了Swagger框架@Operation接口方法的执行日志，将summary属性作为操作名称，将请求信息、响应信息、异常信息等记录到日志中
     *
     * @param joinPoint
     * @return
     */
    // @Around("@annotation(io.swagger.v3.oas.annotations.Operation)")
    @Around("execution(* com.ktjy..*.*(..)) && @annotation(operation)")
    public Object logAround(ProceedingJoinPoint joinPoint, Operation operation) {
        // 获得Request请求属性
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        // 如果不存在Request请求属性，直接执行目标方法
        if (attributes == null) {
            try {
                return joinPoint.proceed();
            } catch (Throwable e) {
                log.error(e.getMessage());
                return null;
            }
        }
        // 获得Request请求对象
        HttpServletRequest request = attributes.getRequest();

        // 获取Header中的令牌
        String token = request.getHeader(KEY_TOKEN);
        // 获取Swagger框架操作名称
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String operationSummary = operation.summary(); // 获取@Operation注解的summary属性信息

        // 日志信息容器
        StringBuilder sb = new StringBuilder();
        // 拼接日志
        sb.append(STR________Line);
        // 请求URL
        sb.append(STR_Request_URL).append(request.getRequestURL());
        // 操作信息
        sb.append(STR_Operation).append(operationSummary);
        // 请求方式
        sb.append(STR_HTTP_Method).append(request.getMethod());
        // 调用Controller的全路径以及执行方法
        sb.append(STR_ClassMethod).append(joinPoint.getSignature().getDeclaringTypeName()).append(STR_d).append(joinPoint.getSignature().getName());
        // 请求的IP
        sb.append(STR__IP_Address).append(request.getRemoteAddr());
        // 头部Token
        sb.append(STR_HeaderToken).append(Objects.isNull(token) ? STR_empty : token);
        // 请求入参
        String args;
        try {
            args = JSON.toJSONString(getParams(joinPoint.getArgs()));
        } catch (Exception e) {
            args = STR_;
        }
        sb.append(STR_RequestArgs).append(args);
        sb.append(STR_n); // 换行，前面是请求信息，后面是响应信息
        long start = System.currentTimeMillis();
        try {
            // 执行目标方法
            Object result = joinPoint.proceed();
            long cost = System.currentTimeMillis() - start; // 记录执行耗时

            // 响应数据
            sb.append(STR_ResponseBody).append(JSON.toJSONString(result));
            // 执行耗时
            sb.append(STR_TimeConsuming).append(cost).append(STR_ms);
            sb.append(STR________Line); // 换行，响应信息记录完毕
            // 记录成功日志（可优化为异步处理）
            log.debug(sb.toString());
            return result;
        } catch (Throwable e) {
            // 执行发生异常
            long cost = System.currentTimeMillis() - start; // 记录执行耗时

            // 异常信息
            sb.append(STR_Throwable).append(e.getMessage());
            // 执行耗时
            sb.append(STR_TimeConsuming).append(cost).append(STR_ms);
            sb.append(STR________Line); // 换行，异常信息记录完毕
            // 记录成功日志（可优化为异步处理）
            log.error(sb.toString());
            return null;
        }
    }

    /**
     * 过滤特殊参数
     *
     * @param args
     * @return
     */
    private Object getParams(Object[] args) {
        if (args == null || args.length == 0) {
            return STR_empty;
        }
        // 过滤掉Request/Response等特殊参数
        return Arrays.stream(args).filter(arg -> !(arg instanceof HttpServletRequest || arg instanceof HttpServletResponse)).collect(Collectors.toList());
    }

}
